﻿/**	@file		lolix_lib.h
 *	@date		(2008-07-06 02:08:39)/(2014-11-16 13:59:43)
 *-----------------------------------------------------------------------------
 *	@version	1.0.7.12
 *	@author		Nick Shallery	(nicknide@gmail.com)
 *	@copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#if			!defined(__LOLIX__LOLIX_LIB__NICK__K_IN_G__)
#define		__LOLIX__LOLIX_LIB__NICK__K_IN_G__
//==============================================================================
//--------------------------------------------------------------------------

#if			!defined(__LOLIX__LOLIX_ERR__K_IN_G__)
	#include	"./lolix_err.h"
#endif	//	!defined(__LOLIX__LOLIX_ERR__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)
	#include	"./lolix_inst.h"
#endif	//	!defined(__LOLIX__LOLIX_INSTANCE__K_IN_G__)

#if			!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)
	#include	"./lolix_macro.h"
#endif	//	!defined(__LOLIX__LOLI_X_MACRO__K_IN_G__)

#if			!defined(__LOLIX__LOLIX_VER__K_IN_G__)
	#include	"./lolix_ver.h"
#endif	//	!defined(__LOLIX__LOLIX_VER__K_IN_G__)


#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)

#if			defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)
	SHOW_MSG("Import Lib : Advapi32.lib");
	#pragma comment(lib, "Advapi32.lib")
#endif	//	defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)

#if			!defined(_WINDOWS_)
	#include	<windows.h>
#endif	//	!defined(_WINDOWS_)

#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#include	<dlfcn.h>
	#include	<fcntl.h>
	#include	<stdlib.h>
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)

namespace lolix
{
	class lolix_lib
	{
		CANNT_COPY_LOLI;
#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		HMODULE				_lib;
		typedef	lx_wchar	pname_type;
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		void*				_lib;
		typedef lx_char		pname_type;
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		lolix::fnLxLoadInst		_lxLoadInst;
		lolix::fnLxFreeLib		_lxFreeLib;

	public:
		LOLIX_INLINE_CALL	lolix_lib(void)
			: _lib(0)
			, _lxLoadInst(&__lxLoadInst_Null)
			, _lxFreeLib(&__lxFreeLib_Null)
		{
		}

		LOLIX_INLINE_CALL	~lolix_lib(void)
		{
			if ( !_lib )
				return;
			__FreeLib();
		}

		LOLIX_INLINE_CALL	bool operator == (const lolix_lib& o)
		{
			return _lib == o._lib;
		}


		LOLIX_INLINE_CALL	bool operator != (const lolix_lib& o)const
		{
			return _lib != o._lib;
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	load(void)
		{
			return __Load_with_cfg_type(LOLIX_CFG__CFG_TYPE);
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	load(size_type cfg_type)
		{
			return __Load_with_cfg_type(cfg_type);
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	load(const pname_type* wcs_file)
		{
			lolix::LOLIX_RETNV rtv = __Load_with_path_name(wcs_file);
			if ( LOLIX_IS_ERROR(rtv) )
				return __Load_with_cfg_type(LOLIX_CFG__CFG_TYPE);
			return rtv;
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	load(const pname_type* wcs_file, size_type cfg_type)
		{
			lolix::LOLIX_RETNV rtv = __Load_with_path_name(wcs_file);
			if ( LOLIX_IS_ERROR(rtv) )
				return __Load_with_cfg_type(cfg_type);
			return rtv;
		}

		LOLIX_INLINE_CALL	void		unload(void)
		{
			__Clear();
		}


		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	lxLoadInst(lolix::lolix_inst** out_ptr, const lolix::lolix_init_param* in_param)
		{
			return _lxLoadInst(out_ptr, in_param);
		}

		LOLIX_INLINE_CALL	LOLIX_RETNV lxLoadInst(lolix_inst** out_ptr)
		{
			lolix_init_param init_param;
			STATIC_ASSERT(lolix_init_param::_ver == 3);
			init_param.sver = init_param._ver;
			init_param.cfg_type = LOLIX_CFG__CFG_TYPE;
			init_param.auto_iload = 1;
			init_param.fill_z = 0;
			init_param.alc = 0;
			init_param.diary = 0;
			const LOLIX_ITF_ID lid(LOLIX_LIB_ID);
			init_param.lid = &lid;
			return _lxLoadInst(out_ptr, &init_param);
		}


		LOLIX_INLINE_CALL	void swap(lolix_lib& o)
		{
			lolix::swap(_lib, o._lib);
			lolix::swap(_lxLoadInst, o._lxLoadInst);
			lolix::swap(_lxFreeLib, o._lxFreeLib);
		}

	private:
		LOLIX_INLINE_CALL	LOLIX_RETNV	__Load_with_cfg_type(lolix::size_type cfg_type)
		{
			pname_type file_name[64];
			size_type start_pos = 0;
			LOLIX_ITF_ID id(LOLIX_LIB_ID);
			make_lib_name(file_name, &start_pos, &id, (char(&)[2])LOLIX_CFG__SIZE_TYPE_BITS_SIZE_STRING, cfg_type);
			LOLIX_RETNV rtv;
			if ( !LOLIX_IS_ERROR(rtv = __Load_with_path_name(file_name)) )
				return rtv;
			file_name[start_pos-4] = 0;
#if			defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)
	#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			#define		__LOLIX_TMP_DEF__HOME_PATH		HKEY_CURRENT_USER
			#define		__LOLIX_TMP_DEF__SYS_PATH		HKEY_LOCAL_MACHINE
	#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			#define		__LOLIX_TMP_DEF__HOME_PATH		getenv("HOME")
			#define		__LOLIX_TMP_DEF__SYS_PATH		0
	#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			pname_type path_buf[MAX_PATH];
			if ( !LOLIX_IS_ERROR(rtv = __Find_lib_path_from_reg(path_buf, __LOLIX_TMP_DEF__HOME_PATH, file_name)) && !LOLIX_IS_ERROR(rtv = __Load_with_path_name(path_buf)) )
				return rtv;
			if ( !LOLIX_IS_ERROR(rtv = __Find_lib_path_from_reg(path_buf, __LOLIX_TMP_DEF__SYS_PATH, file_name)) && !LOLIX_IS_ERROR(rtv = __Load_with_path_name(path_buf)) )
				return rtv;
			#undef	__LOLIX_TMP_DEF__SYS_PATH
			#undef	__LOLIX_TMP_DEF__HOME_PATH
#endif	//	defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)
			return rtv;
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	__Load_with_path_name(const pname_type* wcs_file)
		{
			__Clear();
#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			_lib = ::LoadLibraryW(wcs_file);
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			_lib = dlopen(wcs_file, RTLD_NOW);
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			if ( !_lib )
				return lolix::LOLIX_ERROR_INVALID_FILE;
			return __LoadProc();
		}

#if			defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)
	#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		template <size_type ArrayLen>
		static LOLIX_INLINE_CALL lolix::LOLIX_RETNV __Find_lib_path_from_reg(pname_type (&buf)[ArrayLen], HKEY hKey, pname_type const* file_name)
		{
			HKEY subKey;
			if ( ERROR_SUCCESS != RegOpenKeyW(hKey, L"Software\\lolix\\componts\\", &subKey) )
				return LOLIX_ERROR_FILE_NOT_FOUND;
			DWORD buf_size = sizeof(buf) - sizeof buf[0];
			LSTATUS rv = RegQueryValueExW(subKey, file_name, 0, 0, (LPBYTE)buf, &buf_size);
			RegCloseKey(subKey);
			if ( rv != ERROR_SUCCESS )
				return LOLIX_ERROR_FILE_NOT_FOUND;
			buf[buf_size] = 0;
			return LOLIX_RETNV_OK;
		}

	#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		template <size_type ArrayLen>
		static LOLIX_INLINE_CALL lolix::LOLIX_RETNV __Find_lib_path_from_reg(pname_type (&buf)[ArrayLen], pname_type const* const cfgPath, pname_type const* file_name)
		{
			if ( !cfgPath )
				return LOLIX_ERROR_FILE_NOT_FOUND;

			size_t cfgPath_len = strlen(cfgPath);
			size_t file_name_len = strlen(file_name);
			//	0123456789abcdef0
			//	/lolix/componts/
			size_t total_len = cfgPath_len + file_name_len + 1 + 0x10;
			pname_type* dat = new pname_type[len];
			if ( !dat )
				return LOLIX_ERROR_LESS_MEMORY;
			memcpy(dat, cfgPath, cfgPath_len * sizeof(cfgPath[0]));
			memcpy(dat + cfgPath_len, "/lolix/componts/", 0x10 * sizeof(cfgPath[0]));
			memcpy(dat + cfgPath_len + 0x10, file_name, file_name_len * sizeof(file_name[0]));
			dat[total_len-1] = 0;
			int fid = open(dat, O_RDONLY);
			delete[] dat;
			if ( fid == -1 )
				return LOLIX_ERROR_FILE_NOT_FOUND;
			int rdlen = read(fid, buf, sizeof buf);
			close(fid);
			if ( rdlen <= 0 || rdlen == sizeof buf )
				return LOLIX_ERROR_INVALID_FILE;
			buf[rdlen] = 0;
			return LOLIX_ERROR_UNSUPPORT_INTERFACE;
		}

	#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
#endif	//	defined(LOLIX_DEF__SEARCH_LIB_FROM_REG)

		LOLIX_INLINE_CALL	void	__Clear(void)
		{
			if ( !_lib )
				return;

			__FreeLib();
			_lxLoadInst = &lolix_lib::__lxLoadInst_Null;
			_lxFreeLib = &lolix_lib::__lxFreeLib_Null;
			_lib = 0;
		}

		LOLIX_INLINE_CALL	void	__FreeLib(void)
		{
			if ( LOLIX_IS_ERROR(_lxFreeLib()) )
#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
				::FreeLibrary(_lib);
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
				dlclose(_lib);
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
		}

		LOLIX_INLINE_CALL	lolix::LOLIX_RETNV	__LoadProc(void)
		{
#if			defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#define		__LOLIX_TMP_DEF__GET_PROC_ENTER(lib, name)	::GetProcAddress(lib, name)
#else	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
	#define		__LOLIX_TMP_DEF__GET_PROC_ENTER(lib, name)	dlsym(lib, name)
#endif	//	defined(LOLIX_DEF__PLATFORM_TYPE_WINDOWS)
			LOLIX_DEF__ENTER_SECTION();
			{
				_lxLoadInst = (lolix::fnLxLoadInst)__LOLIX_TMP_DEF__GET_PROC_ENTER(_lib, "lxLoadInst");
				if ( !_lxLoadInst )
					break;
				_lxFreeLib = (lolix::fnLxFreeLib)__LOLIX_TMP_DEF__GET_PROC_ENTER(_lib, "lxFreeLib");
				if ( !_lxFreeLib )
					break;
				return lolix::LOLIX_RETNV_OK;
			}
			LOLIX_DEF__LEAVE_SECTION();
			__Clear();
			return LOLIX_ERROR_INVALID_RESOURCE;
	#undef		__LOLIX_TMP_DEF__GET_PROC_ENTER
		}


		static lolix::LOLIX_RETNV	LOLIX_STD_CALL __lxLoadInst_Null(lolix::lolix_inst** /*out_ptr*/, const lolix::lolix_init_param* /*in_param*/)
		{
			return lolix::LOLIX_ERROR_INVALID_INTERFACE;
		}
		static lolix::LOLIX_RETNV	LOLIX_STD_CALL __lxLoadPlugin_Null(lolix::lolix_inst* /*lx_inst*/, lolix::lolix_plugin_param const*const /*plugin_param*/)
		{
			return lolix::LOLIX_ERROR_INVALID_INTERFACE;
		}
		static lolix::LOLIX_RETNV	LOLIX_STD_CALL __lxFreeLib_Null(void)
		{
			return lolix::LOLIX_ERROR_UNSUPPORT_INTERFACE;
		}

	};
}


namespace lolix{
	LOLIX_INLINE_CALL void swap(lolix_lib& l, lolix_lib& r)
	{
		l.swap(r);
	}
}

//--------------------------------------------------------------------------
//==============================================================================
#endif	//	!defined(__LOLIX__LOLIX_LIB__NICK__K_IN_G__)
